pacman::p_load(
  "data.table",
  "ggplot2",
  "stringr",
  "tidyr",
  "dplyr",
  "ggtext",
  "pheatmap",
  "purrr",
  "here",
  "ggnewscale"
)
source("../src/constants.R")
source("../src/themes.R")
source("../src/utility.R")
direct_label = "Direct"
indirect_label = "Indirect"
sample = "fecal"
motifs_direct <-  fread(paste0("../data/", sample, "/nanomotif/motifs.tsv"))[, directly := TRUE]
motifs_score <- fread(paste0("../data/", sample, "/nanomotif/motifs-scored.tsv"))
|--------------------------------------------------|
|==================================================|
motifs <- merge(
  motifs_score, motifs_direct, all.x = TRUE, by=c("contig", "motif", "mod_position", "mod_type"), suffixes = c("", ".drop"))[
  , directly := fifelse(is.na(directly), indirect_label, direct_label)
]

euk <- fread(paste0("../data/", sample, "/mmlong2_lite/tmp/eukfilt/tiara"))
bin <- fread(paste0("../data/", sample, "/mmlong2_lite/tmp/binning/contig_bin.tsv"), header = FALSE)
colnames(bin) <-  c("contig", "bin")
bin[, n_contigs := .N, by = bin]
tax <- fread(paste0("../data/", sample, "/gtdb-tk_mmlong2/gtdbtk.bac120.summary.tsv"))

motifs_full <- merge(motifs, euk, by.x = "contig", by.y = "sequence_id", all.x = TRUE)[class_fst_stage == "bacteria"] %>% 
  merge(bin, by = "contig", all.x = TRUE) %>% 
  merge(tax, by.x = "bin", by.y = "user_genome", all.x = TRUE)

motifs_full <- motifs_full[
    , mean := n_mod / (n_mod + n_nomod-1)
  ][
    , species := sapply(classification, extract_species_from_gtdbtk)
  ][
    , name := fcase(
      is.na(species), contig,
      !is.na(species), paste0(species, "_", contig)
    )
  ][
    , motif_markdown := paste0(
        str_sub(motif, 1, mod_position),
        "**",
        map(mod_type, function(x) MOD_TYPE_PRETTY[[x]]) %>%  unlist(),
        str_sub(motif, mod_position+1, mod_position+1),
        "**",
        str_sub(motif, mod_position+2, -1)
    )
  ][
    , simple_count := fcase(
      (n_mod + n_nomod) > 10, "",
      (n_mod + n_nomod) <= 10, as.character(n_mod + n_nomod - 1)
    )
  ]
motifs_full_binned <- motifs_full[!is.na(bin)]


bin_motifs <- fread(paste0("../data/", sample, "/nanomotif/bin-motifs.tsv")) %>% 
  merge(tax, by.x = "bin", by.y = "user_genome", all.x = TRUE)
bin_motifs[
    , mean := n_mod_bin / (n_mod_bin + n_nomod_bin-1)
  ][
    , species := sapply(classification, extract_species_from_gtdbtk)
  ][
    , name := fcase(
      is.na(species), bin,
      !is.na(species), paste0(species, "_", bin)
    )
  ][
    , motif_markdown := paste0(
        str_sub(motif, 1, mod_position),
        "**",
        map(mod_type, function(x) MOD_TYPE_PRETTY[[x]]) %>%  unlist(),
        str_sub(motif, mod_position+1, mod_position+1),
        "**",
        str_sub(motif, mod_position+2, -1)
    )
  ]
get_axis_order <- function(wide_df) {
  set.seed(1)
  feature_matrix = as.matrix(wide_df[,-1] )
  y_row_order <- pheatmap::pheatmap(as.matrix(feature_matrix), silent = TRUE)$tree_row$order
  y_factor_order <- wide_df$name[y_row_order]
  x_row_order <- pheatmap::pheatmap(t(as.matrix(feature_matrix)), silent = TRUE)$tree_row$order
  x_factor_order <- colnames(wide_df[,-1])[x_row_order]
  return(list(
    y = y_factor_order,
    x = x_factor_order
  ))
}
y_order <- unique(motif_full[order(bin, mean)]$name)
plot_data <- motif_full[!is.na(n_contigs)]
axis_orders <- get_axis_order(dcast(plot_data, name ~ motif_markdown, value.var = "mean", fill = 0, fun.aggregate = mean))
bin_contigs_heatmap <- ggplot(plot_data) +
  aes(x = factor(motif_markdown, levels = axis_orders[["x"]]), y = factor(name, levels =  axis_orders[["y"]])) +
  geom_tile(aes(fill = directly), color = "#ffffff00", alpha = 0) +
  scale_fill_manual(values = c("Direct"=direct_color, "Indirect"=indirect_color)) +
  guides(fill = guide_legend(
    title = "Method of detection",
    override.aes = list(alpha = 1))
  ) +
  new_scale("fill") +
  
  geom_tile(aes(fill = mean),  color = "#ffffff00",linewidth = 0) +
  scale_fill_gradient2(guide = "none", limits = c(0, 1), low = "white", high = direct_color, na.value = "white") +
  new_scale("fill") +
  
  geom_tile(data=plot_data[directly == indirect_label], aes(fill = mean),  color = "#ffffff00",linewidth = 0) + 
  scale_fill_gradient2(guide = "none", limits = c(0, 1), low = "white", high = indirect_color, na.value = "white") +
  labs(fill = "Directly") +  
  
  new_scale("fill") +
  geom_text(data=plot_data[(n_mod + n_nomod - 1)==0], label = ".", hjust=0.5, vjust=0, size=1) +
  geom_tile(aes(fill = mean), linewidth = 0, alpha = 0, color = "#33333300") +
  scale_fill_gradient2(
    limits = c(0, 1), low = "white", high = "gray20",
    labels = scales::percent
  ) +
  theme(
    axis.text.y = element_blank(),# element_markdown(vjust = 0.5, hjust = 0, size = 0.5),
    axis.ticks.y = element_blank(),
    axis.text.x = element_markdown(angle = 90, hjust = 1, vjust = 0.5, size = 5),
    panel.grid.minor = element_blank(),  # Remove minor gridlines
    panel.background = element_blank(),   # Remove panel background,
    panel.margin = unit(0, "lines"),
    panel.border = element_rect(colour = "#141414", fill=NA, linewidth = 0.1),
    strip.placement = "outside",
    strip.text.y.left = element_markdown(angle = 0, vjust = 0.5, hjust = 0),
    strip.background = element_blank()
  ) +
  labs(
    x = "Motif",
    y = "",
    fill = "Methylation degree"
  ) +
  facet_wrap(~bin, scales = "free_y", ncol = 1, strip.position = "left")+
  geom_point(aes(color = "No motif observations"), shape = 16, size = 1, alpha = 0) +
  #geom_point(data = data.frame(x = NA, y = NA), aes(x = x, y = y, color = "Assigned MTase"), 
  #           shape = 8, size = 1, alpha=0) +
  scale_color_manual(name = "Indicator", values = c("No motif observations" = "black")) +
  guides(color = guide_legend(override.aes = list(alpha = 1, shape = c(16)))) # Adjust this line as needed
bin_contigs_heatmap


axis_orders <- get_axis_order(dcast(motifs_full_binned, name ~ motif_markdown, value.var = "mean", fill = 0, fun.aggregate = mean))
hm_fecal <- ggplot(motifs_full_binned) +
  aes(x = factor(motif_markdown, levels = axis_orders[["x"]]), y = factor(name, levels = axis_orders[["y"]])) + 
  geom_tile(aes(fill = directly), linewidth = 0, alpha = 0) + 
  scale_fill_manual(values = c("Direct"="forestgreen", "Indirect"="#8C228C")) +
  guides(fill = guide_legend(
    title = "Method of detection",
    override.aes = list(alpha = 1))
  ) +
  new_scale("fill") +
  geom_tile(aes(fill = mean), linewidth = 0) +   
  scale_fill_gradient2(guide = "none", limits = c(0, 1), low = "white", high = "forestgreen", na.value = "white") +
  new_scale("fill") +
  geom_tile(data=motifs_full_binned[directly == indirect_label], aes(fill = mean), linewidth = 0) + 
  scale_fill_gradient2(guide = "none", limits = c(0, 1), low = "white", high = "#8C228C", na.value = "white") +
  labs(fill = "Directly") + 
  new_scale("fill") +
  geom_tile(aes(fill = mean), linewidth = 0, color = "gray20", alpha = 0) +
  scale_fill_gradient2(
    limits = c(0, 1), low = "white", high = "gray20",
    labels = scales::percent
  ) +
  #geom_text(data=fecal_binned[(n_mod + n_nomod - 1)==0], label = ".", hjust=0.5, vjust=0, size=0.1) +
  #coord_fixed(ratio= 1, clip="off") +
  theme(
    axis.text.y = element_markdown(vjust = 0.5, hjust = 0, size = 1), # Rotate x-axis labels to 90 degrees
    axis.text.x = element_markdown(angle = 90, hjust = 1, vjust = 0.5, size = 2),
    panel.grid.minor = element_blank(),  # Remove minor gridlines
    panel.background = element_blank(),   # Remove panel background,
  ) +
  labs(
    x = "Motif",
    y = "",
    fill = "Methylation degree"
  )
ggsave(
  "../figures/fecal_hm.png",
  hm_fecal
)
Saving 7 x 7 in image

axis_orders <- get_axis_order(dcast(bin_motifs, name ~ motif_markdown, value.var = "mean", fill = 0, fun.aggregate = mean))
hm_fecal <- ggplot(bin_motifs) +
  aes(x = factor(motif_markdown, levels = axis_orders[["x"]]), y = factor(name, levels = axis_orders[["y"]])) + 
  geom_tile(aes(fill = mean), linewidth = 0) +   
  scale_fill_gradient2(guide = "none", limits = c(0, 1), low = "white", high = "forestgreen", na.value = "white") +
  new_scale("fill") +
  geom_tile(aes(fill = mean), linewidth = 0, color = "gray20", alpha = 0) +
  scale_fill_gradient2(
    limits = c(0, 1), low = "white", high = "gray20",
    labels = scales::percent
  ) +
  #geom_text(data=bin_motifsned[(n_mod + n_nomod - 1)==0], label = ".", hjust=0.5, vjust=0, size=0.1) +
  #coord_fixed(ratio= 1, clip="off") +
  theme(
    axis.text.y = element_markdown(vjust = 0.5, hjust = 0, size = 1), # Rotate x-axis labels to 90 degrees
    axis.text.x = element_markdown(angle = 90, hjust = 1, vjust = 0.5, size = 2),
    panel.grid.minor = element_blank(),  # Remove minor gridlines
    panel.background = element_blank(),   # Remove panel background,
  ) +
  labs(
    x = "Motif",
    y = "",
    fill = "Methylation degree"
  )
hm_fecal

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgoKYGBge3J9CnBhY21hbjo6cF9sb2FkKAogICJkYXRhLnRhYmxlIiwKICAiZ2dwbG90MiIsCiAgInN0cmluZ3IiLAogICJ0aWR5ciIsCiAgImRwbHlyIiwKICAiZ2d0ZXh0IiwKICAicGhlYXRtYXAiLAogICJwdXJyciIsCiAgImhlcmUiLAogICJnZ25ld3NjYWxlIgopCnNvdXJjZSgiLi4vc3JjL2NvbnN0YW50cy5SIikKc291cmNlKCIuLi9zcmMvdGhlbWVzLlIiKQpzb3VyY2UoIi4uL3NyYy91dGlsaXR5LlIiKQpkaXJlY3RfbGFiZWwgPSAiRGlyZWN0IgppbmRpcmVjdF9sYWJlbCA9ICJJbmRpcmVjdCIKc2FtcGxlID0gImFuYWVyb2JpY19kaWdlc3RvciIKYGBgCgoKYGBge3J9Cm1vdGlmc19kaXJlY3QgPC0gIGZyZWFkKHBhc3RlMCgiLi4vZGF0YS8iLCBzYW1wbGUsICIvbmFub21vdGlmL21vdGlmcy50c3YiKSlbLCBkaXJlY3RseSA6PSBUUlVFXQptb3RpZnNfc2NvcmUgPC0gZnJlYWQocGFzdGUwKCIuLi9kYXRhLyIsIHNhbXBsZSwgIi9uYW5vbW90aWYvbW90aWZzLXNjb3JlZC50c3YiKSkKbW90aWZzIDwtIG1lcmdlKAogIG1vdGlmc19zY29yZSwgbW90aWZzX2RpcmVjdCwgYWxsLnggPSBUUlVFLCBieT1jKCJjb250aWciLCAibW90aWYiLCAibW9kX3Bvc2l0aW9uIiwgIm1vZF90eXBlIiksIHN1ZmZpeGVzID0gYygiIiwgIi5kcm9wIikpWwogICwgZGlyZWN0bHkgOj0gZmlmZWxzZShpcy5uYShkaXJlY3RseSksIGluZGlyZWN0X2xhYmVsLCBkaXJlY3RfbGFiZWwpCl0KCmV1ayA8LSBmcmVhZChwYXN0ZTAoIi4uL2RhdGEvIiwgc2FtcGxlLCAiL21tbG9uZzJfbGl0ZS90bXAvZXVrZmlsdC90aWFyYSIpKQpiaW4gPC0gZnJlYWQocGFzdGUwKCIuLi9kYXRhLyIsIHNhbXBsZSwgIi9tbWxvbmcyX2xpdGUvdG1wL2Jpbm5pbmcvY29udGlnX2Jpbi50c3YiKSwgaGVhZGVyID0gRkFMU0UpCmNvbG5hbWVzKGJpbikgPC0gIGMoImNvbnRpZyIsICJiaW4iKQpiaW5bLCBuX2NvbnRpZ3MgOj0gLk4sIGJ5ID0gYmluXQp0YXggPC0gZnJlYWQocGFzdGUwKCIuLi9kYXRhLyIsIHNhbXBsZSwgIi9ndGRiLXRrX21tbG9uZzIvZ3RkYnRrLmJhYzEyMC5zdW1tYXJ5LnRzdiIpKQoKbW90aWZzX2Z1bGwgPC0gbWVyZ2UobW90aWZzLCBldWssIGJ5LnggPSAiY29udGlnIiwgYnkueSA9ICJzZXF1ZW5jZV9pZCIsIGFsbC54ID0gVFJVRSlbY2xhc3NfZnN0X3N0YWdlID09ICJiYWN0ZXJpYSJdICU+JSAKICBtZXJnZShiaW4sIGJ5ID0gImNvbnRpZyIsIGFsbC54ID0gVFJVRSkgJT4lIAogIG1lcmdlKHRheCwgYnkueCA9ICJiaW4iLCBieS55ID0gInVzZXJfZ2Vub21lIiwgYWxsLnggPSBUUlVFKQoKbW90aWZzX2Z1bGwgPC0gbW90aWZzX2Z1bGxbCiAgICAsIG1lYW4gOj0gbl9tb2QgLyAobl9tb2QgKyBuX25vbW9kLTEpCiAgXVsKICAgICwgc3BlY2llcyA6PSBzYXBwbHkoY2xhc3NpZmljYXRpb24sIGV4dHJhY3Rfc3BlY2llc19mcm9tX2d0ZGJ0aykKICBdWwogICAgLCBuYW1lIDo9IGZjYXNlKAogICAgICBpcy5uYShzcGVjaWVzKSwgY29udGlnLAogICAgICAhaXMubmEoc3BlY2llcyksIHBhc3RlMChzcGVjaWVzLCAiXyIsIGNvbnRpZykKICAgICkKICBdWwogICAgLCBtb3RpZl9tYXJrZG93biA6PSBwYXN0ZTAoCiAgICAgICAgc3RyX3N1Yihtb3RpZiwgMSwgbW9kX3Bvc2l0aW9uKSwKICAgICAgICAiKioiLAogICAgICAgIG1hcChtb2RfdHlwZSwgZnVuY3Rpb24oeCkgTU9EX1RZUEVfUFJFVFRZW1t4XV0pICU+JSAgdW5saXN0KCksCiAgICAgICAgc3RyX3N1Yihtb3RpZiwgbW9kX3Bvc2l0aW9uKzEsIG1vZF9wb3NpdGlvbisxKSwKICAgICAgICAiKioiLAogICAgICAgIHN0cl9zdWIobW90aWYsIG1vZF9wb3NpdGlvbisyLCAtMSkKICAgICkKICBdWwogICAgLCBzaW1wbGVfY291bnQgOj0gZmNhc2UoCiAgICAgIChuX21vZCArIG5fbm9tb2QpID4gMTAsICIiLAogICAgICAobl9tb2QgKyBuX25vbW9kKSA8PSAxMCwgYXMuY2hhcmFjdGVyKG5fbW9kICsgbl9ub21vZCAtIDEpCiAgICApCiAgXQptb3RpZnNfZnVsbF9iaW5uZWQgPC0gbW90aWZzX2Z1bGxbIWlzLm5hKGJpbildCgoKYmluX21vdGlmcyA8LSBmcmVhZChwYXN0ZTAoIi4uL2RhdGEvIiwgc2FtcGxlLCAiL25hbm9tb3RpZi9iaW4tbW90aWZzLnRzdiIpKSAlPiUgCiAgbWVyZ2UodGF4LCBieS54ID0gImJpbiIsIGJ5LnkgPSAidXNlcl9nZW5vbWUiLCBhbGwueCA9IFRSVUUpCmJpbl9tb3RpZnNbCiAgICAsIG1lYW4gOj0gbl9tb2RfYmluIC8gKG5fbW9kX2JpbiArIG5fbm9tb2RfYmluLTEpCiAgXVsKICAgICwgc3BlY2llcyA6PSBzYXBwbHkoY2xhc3NpZmljYXRpb24sIGV4dHJhY3Rfc3BlY2llc19mcm9tX2d0ZGJ0aykKICBdWwogICAgLCBuYW1lIDo9IGZjYXNlKAogICAgICBpcy5uYShzcGVjaWVzKSwgYmluLAogICAgICAhaXMubmEoc3BlY2llcyksIHBhc3RlMChzcGVjaWVzLCAiXyIsIGJpbikKICAgICkKICBdWwogICAgLCBtb3RpZl9tYXJrZG93biA6PSBwYXN0ZTAoCiAgICAgICAgc3RyX3N1Yihtb3RpZiwgMSwgbW9kX3Bvc2l0aW9uKSwKICAgICAgICAiKioiLAogICAgICAgIG1hcChtb2RfdHlwZSwgZnVuY3Rpb24oeCkgTU9EX1RZUEVfUFJFVFRZW1t4XV0pICU+JSAgdW5saXN0KCksCiAgICAgICAgc3RyX3N1Yihtb3RpZiwgbW9kX3Bvc2l0aW9uKzEsIG1vZF9wb3NpdGlvbisxKSwKICAgICAgICAiKioiLAogICAgICAgIHN0cl9zdWIobW90aWYsIG1vZF9wb3NpdGlvbisyLCAtMSkKICAgICkKICBdCmBgYAoKCmBgYHtyfQpnZXRfYXhpc19vcmRlciA8LSBmdW5jdGlvbih3aWRlX2RmKSB7CiAgc2V0LnNlZWQoMSkKICBmZWF0dXJlX21hdHJpeCA9IGFzLm1hdHJpeCh3aWRlX2RmWywtMV0gKQogIHlfcm93X29yZGVyIDwtIHBoZWF0bWFwOjpwaGVhdG1hcChhcy5tYXRyaXgoZmVhdHVyZV9tYXRyaXgpLCBzaWxlbnQgPSBUUlVFKSR0cmVlX3JvdyRvcmRlcgogIHlfZmFjdG9yX29yZGVyIDwtIHdpZGVfZGYkbmFtZVt5X3Jvd19vcmRlcl0KICB4X3Jvd19vcmRlciA8LSBwaGVhdG1hcDo6cGhlYXRtYXAodChhcy5tYXRyaXgoZmVhdHVyZV9tYXRyaXgpKSwgc2lsZW50ID0gVFJVRSkkdHJlZV9yb3ckb3JkZXIKICB4X2ZhY3Rvcl9vcmRlciA8LSBjb2xuYW1lcyh3aWRlX2RmWywtMV0pW3hfcm93X29yZGVyXQogIHJldHVybihsaXN0KAogICAgeSA9IHlfZmFjdG9yX29yZGVyLAogICAgeCA9IHhfZmFjdG9yX29yZGVyCiAgKSkKfQoKYGBgCgpgYGB7cn0KeV9vcmRlciA8LSB1bmlxdWUobW90aWZzX2Z1bGxbb3JkZXIoYmluLCBtZWFuKV0kbmFtZSkKcGxvdF9kYXRhIDwtIG1vdGlmc19mdWxsWyFpcy5uYShuX2NvbnRpZ3MpXQpheGlzX29yZGVycyA8LSBnZXRfYXhpc19vcmRlcihkY2FzdChwbG90X2RhdGEsIG5hbWUgfiBtb3RpZl9tYXJrZG93biwgdmFsdWUudmFyID0gIm1lYW4iLCBmaWxsID0gMCwgZnVuLmFnZ3JlZ2F0ZSA9IG1lYW4pKQpiaW5fY29udGlnc19oZWF0bWFwIDwtIGdncGxvdChwbG90X2RhdGEpICsKICBhZXMoeCA9IGZhY3Rvcihtb3RpZl9tYXJrZG93biwgbGV2ZWxzID0gYXhpc19vcmRlcnNbWyJ4Il1dKSwgeSA9IGZhY3RvcihuYW1lLCBsZXZlbHMgPSAgYXhpc19vcmRlcnNbWyJ5Il1dKSkgKwogIGdlb21fdGlsZShhZXMoZmlsbCA9IGRpcmVjdGx5KSwgY29sb3IgPSAiI2ZmZmZmZjAwIiwgYWxwaGEgPSAwKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiRGlyZWN0Ij1kaXJlY3RfY29sb3IsICJJbmRpcmVjdCI9aW5kaXJlY3RfY29sb3IpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQoCiAgICB0aXRsZSA9ICJNZXRob2Qgb2YgZGV0ZWN0aW9uIiwKICAgIG92ZXJyaWRlLmFlcyA9IGxpc3QoYWxwaGEgPSAxKSkKICApICsKICBuZXdfc2NhbGUoImZpbGwiKSArCiAgCiAgZ2VvbV90aWxlKGFlcyhmaWxsID0gbWVhbiksICBjb2xvciA9ICIjZmZmZmZmMDAiLGxpbmV3aWR0aCA9IDApICsKICBzY2FsZV9maWxsX2dyYWRpZW50MihndWlkZSA9ICJub25lIiwgbGltaXRzID0gYygwLCAxKSwgbG93ID0gIndoaXRlIiwgaGlnaCA9IGRpcmVjdF9jb2xvciwgbmEudmFsdWUgPSAid2hpdGUiKSArCiAgbmV3X3NjYWxlKCJmaWxsIikgKwogIAogIGdlb21fdGlsZShkYXRhPXBsb3RfZGF0YVtkaXJlY3RseSA9PSBpbmRpcmVjdF9sYWJlbF0sIGFlcyhmaWxsID0gbWVhbiksICBjb2xvciA9ICIjZmZmZmZmMDAiLGxpbmV3aWR0aCA9IDApICsgCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIoZ3VpZGUgPSAibm9uZSIsIGxpbWl0cyA9IGMoMCwgMSksIGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSBpbmRpcmVjdF9jb2xvciwgbmEudmFsdWUgPSAid2hpdGUiKSArCiAgbGFicyhmaWxsID0gIkRpcmVjdGx5IikgKyAgCiAgCiAgbmV3X3NjYWxlKCJmaWxsIikgKwogIGdlb21fdGV4dChkYXRhPXBsb3RfZGF0YVsobl9tb2QgKyBuX25vbW9kIC0gMSk9PTBdLCBsYWJlbCA9ICIuIiwgaGp1c3Q9MC41LCB2anVzdD0wLCBzaXplPTEpICsKICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBtZWFuKSwgbGluZXdpZHRoID0gMCwgYWxwaGEgPSAwLCBjb2xvciA9ICIjMzMzMzMzMDAiKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIoCiAgICBsaW1pdHMgPSBjKDAsIDEpLCBsb3cgPSAid2hpdGUiLCBoaWdoID0gImdyYXkyMCIsCiAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQKICApICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLCMgZWxlbWVudF9tYXJrZG93bih2anVzdCA9IDAuNSwgaGp1c3QgPSAwLCBzaXplID0gMC41KSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9tYXJrZG93bihhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41LCBzaXplID0gNSksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBSZW1vdmUgbWlub3IgZ3JpZGxpbmVzCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAgICMgUmVtb3ZlIHBhbmVsIGJhY2tncm91bmQsCiAgICBwYW5lbC5tYXJnaW4gPSB1bml0KDAsICJsaW5lcyIpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICIjMTQxNDE0IiwgZmlsbD1OQSwgbGluZXdpZHRoID0gMC4xKSwKICAgIHN0cmlwLnBsYWNlbWVudCA9ICJvdXRzaWRlIiwKICAgIHN0cmlwLnRleHQueS5sZWZ0ID0gZWxlbWVudF9tYXJrZG93bihhbmdsZSA9IDAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDApLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKQogICkgKwogIGxhYnMoCiAgICB4ID0gIk1vdGlmIiwKICAgIHkgPSAiIiwKICAgIGZpbGwgPSAiTWV0aHlsYXRpb24gZGVncmVlIgogICkgKwogIGZhY2V0X3dyYXAofmJpbiwgc2NhbGVzID0gImZyZWVfeSIsIG5jb2wgPSAxLCBzdHJpcC5wb3NpdGlvbiA9ICJsZWZ0IikrCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSAiTm8gbW90aWYgb2JzZXJ2YXRpb25zIiksIHNoYXBlID0gMTYsIHNpemUgPSAxLCBhbHBoYSA9IDApICsKICAjZ2VvbV9wb2ludChkYXRhID0gZGF0YS5mcmFtZSh4ID0gTkEsIHkgPSBOQSksIGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gIkFzc2lnbmVkIE1UYXNlIiksIAogICMgICAgICAgICAgIHNoYXBlID0gOCwgc2l6ZSA9IDEsIGFscGhhPTApICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJJbmRpY2F0b3IiLCB2YWx1ZXMgPSBjKCJObyBtb3RpZiBvYnNlcnZhdGlvbnMiID0gImJsYWNrIikpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChhbHBoYSA9IDEsIHNoYXBlID0gYygxNikpKSkgIyBBZGp1c3QgdGhpcyBsaW5lIGFzIG5lZWRlZAoKaGVpZ2h0X2htIDwtIDQuNQp3aWR0aF9obSA8LSA4CnBsb3RfbmFtZSA8LSAiZmVjYWxfY29tcGxleF9obSIKZ2dzYXZlKAogIHBhc3RlMCgiLi4vZmlndXJlcy8iLCBwbG90X25hbWUsICIucG5nIiksCiAgYmluX2NvbnRpZ3NfaGVhdG1hcCwKICBoZWlnaHQgPSBoZWlnaHRfaG0sCiAgd2lkdGggPSB3aWR0aF9obSwKICBkcGkgPSAicmV0aW5hIiwKICBiZyA9ICJ3aGl0ZSIpCmBgYAoKCgpgYGB7cn0KCmF4aXNfb3JkZXJzIDwtIGdldF9heGlzX29yZGVyKGRjYXN0KG1vdGlmc19mdWxsX2Jpbm5lZCwgbmFtZSB+IG1vdGlmX21hcmtkb3duLCB2YWx1ZS52YXIgPSAibWVhbiIsIGZpbGwgPSAwLCBmdW4uYWdncmVnYXRlID0gbWVhbikpCmhtIDwtIGdncGxvdChtb3RpZnNfZnVsbF9iaW5uZWQpICsKICBhZXMoeCA9IGZhY3Rvcihtb3RpZl9tYXJrZG93biwgbGV2ZWxzID0gYXhpc19vcmRlcnNbWyJ4Il1dKSwgeSA9IGZhY3RvcihuYW1lLCBsZXZlbHMgPSBheGlzX29yZGVyc1tbInkiXV0pKSArIAogIGdlb21fdGlsZShhZXMoZmlsbCA9IGRpcmVjdGx5KSwgbGluZXdpZHRoID0gMCwgYWxwaGEgPSAwKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkRpcmVjdCI9ImZvcmVzdGdyZWVuIiwgIkluZGlyZWN0Ij0iIzhDMjI4QyIpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQoCiAgICB0aXRsZSA9ICJNZXRob2Qgb2YgZGV0ZWN0aW9uIiwKICAgIG92ZXJyaWRlLmFlcyA9IGxpc3QoYWxwaGEgPSAxKSkKICApICsKICBuZXdfc2NhbGUoImZpbGwiKSArCiAgZ2VvbV90aWxlKGFlcyhmaWxsID0gbWVhbiksIGxpbmV3aWR0aCA9IDApICsgICAKICBzY2FsZV9maWxsX2dyYWRpZW50MihndWlkZSA9ICJub25lIiwgbGltaXRzID0gYygwLCAxKSwgbG93ID0gIndoaXRlIiwgaGlnaCA9ICJmb3Jlc3RncmVlbiIsIG5hLnZhbHVlID0gIndoaXRlIikgKwogIG5ld19zY2FsZSgiZmlsbCIpICsKICBnZW9tX3RpbGUoZGF0YT1tb3RpZnNfZnVsbF9iaW5uZWRbZGlyZWN0bHkgPT0gaW5kaXJlY3RfbGFiZWxdLCBhZXMoZmlsbCA9IG1lYW4pLCBsaW5ld2lkdGggPSAwKSArIAogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGd1aWRlID0gIm5vbmUiLCBsaW1pdHMgPSBjKDAsIDEpLCBsb3cgPSAid2hpdGUiLCBoaWdoID0gIiM4QzIyOEMiLCBuYS52YWx1ZSA9ICJ3aGl0ZSIpICsKICBsYWJzKGZpbGwgPSAiRGlyZWN0bHkiKSArIAogIG5ld19zY2FsZSgiZmlsbCIpICsKICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBtZWFuKSwgbGluZXdpZHRoID0gMCwgY29sb3IgPSAiZ3JheTIwIiwgYWxwaGEgPSAwKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIoCiAgICBsaW1pdHMgPSBjKDAsIDEpLCBsb3cgPSAid2hpdGUiLCBoaWdoID0gImdyYXkyMCIsCiAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQKICApICsKICAjZ2VvbV90ZXh0KGRhdGE9ZmVjYWxfYmlubmVkWyhuX21vZCArIG5fbm9tb2QgLSAxKT09MF0sIGxhYmVsID0gIi4iLCBoanVzdD0wLjUsIHZqdXN0PTAsIHNpemU9MC4xKSArCiAgI2Nvb3JkX2ZpeGVkKHJhdGlvPSAxLCBjbGlwPSJvZmYiKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfbWFya2Rvd24odmp1c3QgPSAwLjUsIGhqdXN0ID0gMCwgc2l6ZSA9IDEpLCAjIFJvdGF0ZSB4LWF4aXMgbGFiZWxzIHRvIDkwIGRlZ3JlZXMKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9tYXJrZG93bihhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41LCBzaXplID0gMiksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBSZW1vdmUgbWlub3IgZ3JpZGxpbmVzCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAgICMgUmVtb3ZlIHBhbmVsIGJhY2tncm91bmQsCiAgKSArCiAgbGFicygKICAgIHggPSAiTW90aWYiLAogICAgeSA9ICIiLAogICAgZmlsbCA9ICJNZXRoeWxhdGlvbiBkZWdyZWUiCiAgKQpgYGAKCgoKYGBge3J9Cmdnc2F2ZSgKICAiLi4vZmlndXJlcy9iaW5uZWRfY29udGlnX2hlYXRtYXBfIiwgc2FtcGxlLCAiLnBuZyIsCiAgaG0sCiAgd2lkdGggPSAyNSwKICBoZWlnaHQgPSAyNQopCmBgYApgYGB7cn0KCmF4aXNfb3JkZXJzIDwtIGdldF9heGlzX29yZGVyKGRjYXN0KGJpbl9tb3RpZnMsIG5hbWUgfiBtb3RpZl9tYXJrZG93biwgdmFsdWUudmFyID0gIm1lYW4iLCBmaWxsID0gMCwgZnVuLmFnZ3JlZ2F0ZSA9IG1lYW4pKQpobV9mZWNhbCA8LSBnZ3Bsb3QoYmluX21vdGlmcykgKwogIGFlcyh4ID0gZmFjdG9yKG1vdGlmX21hcmtkb3duLCBsZXZlbHMgPSBheGlzX29yZGVyc1tbIngiXV0pLCB5ID0gZmFjdG9yKG5hbWUsIGxldmVscyA9IGF4aXNfb3JkZXJzW1sieSJdXSkpICsgCiAgZ2VvbV90aWxlKGFlcyhmaWxsID0gbWVhbiksIGxpbmV3aWR0aCA9IDApICsgICAKICBzY2FsZV9maWxsX2dyYWRpZW50MihndWlkZSA9ICJub25lIiwgbGltaXRzID0gYygwLCAxKSwgbG93ID0gIndoaXRlIiwgaGlnaCA9ICJmb3Jlc3RncmVlbiIsIG5hLnZhbHVlID0gIndoaXRlIikgKwogIG5ld19zY2FsZSgiZmlsbCIpICsKICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBtZWFuKSwgbGluZXdpZHRoID0gMCwgY29sb3IgPSAiZ3JheTIwIiwgYWxwaGEgPSAwKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIoCiAgICBsaW1pdHMgPSBjKDAsIDEpLCBsb3cgPSAid2hpdGUiLCBoaWdoID0gImdyYXkyMCIsCiAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQKICApICsKICAjZ2VvbV90ZXh0KGRhdGE9YmluX21vdGlmc25lZFsobl9tb2QgKyBuX25vbW9kIC0gMSk9PTBdLCBsYWJlbCA9ICIuIiwgaGp1c3Q9MC41LCB2anVzdD0wLCBzaXplPTAuMSkgKwogICNjb29yZF9maXhlZChyYXRpbz0gMSwgY2xpcD0ib2ZmIikgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X21hcmtkb3duKHZqdXN0ID0gMC41LCBoanVzdCA9IDAsIHNpemUgPSAxKSwgIyBSb3RhdGUgeC1heGlzIGxhYmVscyB0byA5MCBkZWdyZWVzCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfbWFya2Rvd24oYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDIpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgUmVtb3ZlIG1pbm9yIGdyaWRsaW5lcwogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgICAjIFJlbW92ZSBwYW5lbCBiYWNrZ3JvdW5kLAogICkgKwogIGxhYnMoCiAgICB4ID0gIk1vdGlmIiwKICAgIHkgPSAiIiwKICAgIGZpbGwgPSAiTWV0aHlsYXRpb24gZGVncmVlIgogICkKaG1fZmVjYWwKYGBgCgpgYGB7cn0KYmluX21vdGlmc1sKICAsICBjKCJkb21haW4iLCAicGh5bHVtIiwgImNsYXNzIiwgIm9yZGVyIiwgImZhbWlseSIsICJnZW51cyIsICJzcGVjaWVzIikgOj0gZXh0cmF4dF9ndGRiX3RheChjbGFzc2lmaWNhdGlvbikKXQpgYGAKCgoKCgpgYGB7cn0KZ2dwbG90KGJpbl9tb3RpZnMpICsgCiAgZ2VvbV9iYXIoYWVzKHg9bW9kX3R5cGUsIGZpbGwgPSBtb3RpZl90eXBlKSkgKwogIGJhc2VfcGxvdF90aGVtZSgpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiKQogIApgYGAKCmBgYHtyfQpnZ3Bsb3QoCiAgYmluX21vdGlmc1ssIC4oCiAgICBuX21vdGlmcyA9IC5OCiAgKSwgYnkgPSAuKGJpbiwgbW9kX3R5cGUpXQopICsgCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBuX21vdGlmcykpICsgCiAgYmFzZV9wbG90X3RoZW1lKCkgKyAKICB4bGltKDAsIDgpICsKICBmYWNldF93cmFwKH5tb2RfdHlwZSkKYGBgCgoKYGBge3J9CmdncGxvdChiaW5fbW90aWZzKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9bWVhbikpICsKICB4bGltKDAsIDEpIAogIApgYGAKCgpgYGB7cn0KYmluX21vdGlmcwpgYGAKCgoKCgoKCgoKCgoKCgoKCg==